Skip to content

[#484] Apply SQLite schema without the native Prisma schema-engine#485

Merged
realproject7 merged 1 commit into
mainfrom
task/484-prisma-schema-engine
Jun 5, 2026
Merged

[#484] Apply SQLite schema without the native Prisma schema-engine#485
realproject7 merged 1 commit into
mainfrom
task/484-prisma-schema-engine

Conversation

@realproject7

Copy link
Copy Markdown
Owner

#484 — Fix packed prod-only Prisma schema-engine startup failure

Follow-up for EPIC #465; unblocks operator gate #472. Builds on #479 (PR #480) and #481 (PR #483) — does not revert either.

Root cause

Even after #479/#481, the operator's final verification on main 1.2.93 (Node 20.20.2 / npm 10.8.2, macOS arm64) failed in the packed start smoke: startup prisma db push exited with an empty Schema engine error: before /api/auth/status served.

prisma db push spawns the native Prisma schema-engine — a separate, platform-specific binary (@prisma/engines/schema-engine-*). That binary fails to start in some packed prod-only installs (empty error on macOS arm64). #479 made the CLI resolution robust, but the schema-engine itself is the fragile piece, and it only runs db push. (This is why the Linux start smoke went green for #479/#481 — Linux's schema-engine works.)

Fix — stop using the schema-engine at runtime

The app's normal operation uses the Prisma client's library query engine (a different, reliably-present engine — if it failed, the app couldn't serve anything). So we let it set up the schema instead of the schema-engine:

  • app/prisma/schema.sql (new, committed): the canonical DDL generated from schema.prisma. Regenerate with npm run prisma:sql (scripts/gen-schema-sql.mjs) — that uses the schema-engine at dev/build time only, never at user runtime.
  • app/lib/apply-schema.ts (new): parseSqlStatements / makeIdempotent / loadSchemaStatements — reads the DDL and rewrites each CREATE TABLE/INDEX to IF NOT EXISTS (idempotent on every boot).
  • app/server.ts: connect the client, then apply the DDL via db.$executeRawUnsafe(...). No prisma db push, no child-process shell-out for DB setup. Keeps the HOME-redacted diagnostic + exit 1.
  • Removed now-unused app/lib/prisma-cli.ts (+test): the runtime no longer invokes any Prisma binary, which strictly supersedes Fix packed tarball startup smoke failing at Prisma db push #479's CLI-resolution (it does not reintroduce the npx network/cwd bug Fix packed tarball startup smoke failing at Prisma db push #479 fixed). Pack-hygiene allowlist swaps that entry for app/lib/apply-schema.ts + app/prisma/schema.sql.

Proof it removes the schema-engine dependency

Beyond preflight passing, I ran a decisive check: in a packed --omit=dev install I deleted the schema-engine binary (@prisma/engines/schema-engine-debian-openssl-3.0.x, the only one present) and started the bin with a fresh HOME — the app still booted and served /api/auth/status. The old prisma db push path would fail there (that's the operator's exact symptom). Platform-independent fix confirmed.

Regression coverage

  • Idempotency + parse unit tests for apply-schema.
  • Model→table sync test: every model in schema.prisma has a CREATE TABLE in schema.sql (catches a schema change that forgot npm run prisma:sql).
  • Source contract test: app/server.ts applies schema.sql and never runs db push/execSync — because the Linux start smoke passes either way, this is what locks the fix against a schema-engine regression.

Boundary preserved

Runtime dependencies allowlist unchanged (still 11; prisma stays for the postinstall prisma generate, which uses the query engine and already works). No React/Vite/AWS/S3/build deps added. No wallet/publish/dashboard/fiction/cartoon/agent-session/web-UI behavior changed.

Verification (Node 20.20.2 / npm 10.8.2)

  • npm run typecheck — pass
  • npm test — pass (1261; +7 net)
  • npm run app:build — pass (committed dist unchanged)
  • npm run preflight — pass, 0 warnings / 0 failures (packed start smoke boots + serves /api/auth/status + /)
  • npm audit --omit=dev — 0 vulnerabilities
  • schema-engine-deleted boot proof — pass

Version 1.2.93 → 1.2.94.

Acceptance mapping (#484)

  • Diagnosed exact schema-engine failure (root cause above)
  • Fixed packed install/start path without weakening dependency hygiene
  • Focused regression coverage added (sync + idempotency + source contract)
  • typecheck / test / app:build / preflight pass; npm audit --omit=dev clean
  • Operator Gate: final npm publish readiness check, no publish #472 stays open until the operator reruns final publish-readiness verification

🤖 Generated with Claude Code

…PIC #465)

The packed prod-only start smoke still failed on the operator's macOS arm64:
startup `prisma db push` exited with an empty "Schema engine error:". Root
cause — `db push` spawns the native Prisma *schema-engine* binary, which fails
to start in some packed prod-only installs. #479 had made the CLI *resolution*
robust, but the schema-engine itself is the fragile, platform-specific piece.

Fix: stop invoking the schema-engine at runtime. Ship the canonical DDL as
app/prisma/schema.sql (generated from schema.prisma) and apply it at startup
through the Prisma client's library QUERY engine via $executeRawUnsafe — the
same engine the app already uses for every query, so if the app runs at all,
schema setup runs too. Statements are CREATE TABLE/INDEX IF NOT EXISTS, so it's
idempotent on every boot.

- app/lib/apply-schema.ts (+test): parse/idempotency helpers + loadSchemaStatements.
- app/prisma/schema.sql: committed DDL; scripts/gen-schema-sql.mjs + `npm run
  prisma:sql` regenerate it (schema-engine used at DEV time only).
- app/server.ts: apply schema.sql via the client; no child-process shell-out.
- Remove now-unused app/lib/prisma-cli.ts (+test); supersede its pack-hygiene
  entry with apply-schema.ts + schema.sql.
- Regression coverage: model→table sync test, idempotency tests, and a source
  contract asserting startup never runs `db push` (the Linux smoke can't catch a
  schema-engine regression since its engine works).

Proven: in a packed --omit=dev install with the schema-engine binary DELETED,
the app still boots and serves /api/auth/status (old `db push` would fail there).
No dependency-hygiene change (allowlist still 11; prisma stays for postinstall
generate). Verified Node 20.20.2 / npm 10.8.2: typecheck, test (1261), app:build,
preflight (0/0, packed start smoke green), npm audit --omit=dev clean.
Version 1.2.93 -> 1.2.94.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@project7-interns project7-interns left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verdict: APPROVE

Summary

PR #485 satisfies #484. It removes the runtime dependency on Prisma schema-engine startup by applying committed SQLite DDL through Prisma Client, while preserving the production dependency boundary and packed start-smoke coverage.

Findings

  • No blocking findings.

Decision

Approved at b565d52f33ccdc1196d2a7789f5056b571c6cb01, pending green CI. I reviewed the live PR diff, issue #484, and live checks. The startup path no longer invokes prisma db push or shells out for database setup; app/server.ts connects Prisma Client, loads app/prisma/schema.sql, and applies idempotent CREATE TABLE/INDEX IF NOT EXISTS statements through $executeRawUnsafe. The packed runtime now explicitly requires app/lib/apply-schema.ts and app/prisma/schema.sql, while React, Vite, AWS/S3, and other build/web deps remain out of runtime dependencies. Regression coverage locks the parser/idempotency behavior, committed DDL model coverage, package contents, and the no-db push startup contract. Live lint-and-typecheck was still pending when reviewed. Local tests were not run in this checkout.

@realproject7

Copy link
Copy Markdown
Owner Author

@re2 APPROVE#484 correctly fixes the schema-engine startup failure. Well-engineered with decisive proof.

Reviewed the PR commit (b565d52):

  • Approach is sound. Replacing prisma db push (fragile native schema-engine) with the committed DDL applied via the client's library query engine (db.$executeRawUnsafe, per-statement loop, CREATE … IF NOT EXISTS) is the right call — the query engine is already required for every app query, so "if the app runs, schema setup runs." The proof (packed --omit=dev install with the schema-engine binary deleted, app still boots + serves) directly reproduces and clears the operator's macOS arm64 symptom.
  • Ordering is safe. initDb() (just db.$connect(), no table reads) is moved before the DDL loop — connecting doesn't require tables, so a fresh DB is fine.
  • schema.sql matches schema.prisma. Both models present — Session (id/token/createdAt/expiresAt) + Setting (key/value) + the Session_token_key UNIQUE index for token @unique. Idempotency helpers + the model→table sync test guard against a forgotten npm run prisma:sql.
  • Required-set consistency is correct (the Add npm package hygiene baseline and release preflight checks #466/Stabilize published CLI runtime versus build-time dependency boundary #470 lesson): the now-deleted app/lib/prisma-cli.ts is removed from REQUIRED_PACK_FILES, and apply-schema.ts + schema.sql are added — confirmed both pack (857B/2.2kB), tests excluded, prisma-cli gone. The source-contract test (server.ts has loadSchemaStatements, no db push/execSync) locks the fix; confirmed no other execSync in server.ts so it won't false-fail.
  • Boundary preserved: deps still 11 (prisma stays for postinstall generate); no AWS/React/Vite. Lock + version synced 1.2.94.

Two NON-BLOCKING, forward-looking notes (schema-evolution, not this PR's scope — current Session/Setting schema is unaffected):

  1. CREATE … IF NOT EXISTS won't ALTER an existing table, so a future column addition won't migrate existing users' DBs the way db push would. Worth tracking an explicit migration path before any non-additive/seeded schema change.
  2. The model→table sync test asserts table-NAME presence, not columns — a column drift (new field in schema.prisma without prisma:sql) would slip through. A regen-and-diff or column-level assertion would close that gap later.

No blocking findings.

Note for @Head: CI lint-and-typecheck was still pending at review time — confirm green before merge.

@realproject7 realproject7 merged commit db8c78f into main Jun 5, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants